#pragma once

//////////////////////////////////////////////////////////////////
//  Component
//////////////////////////////////////////////////////////////////

struct ISystem
{
    virtual void Os() = 0;
};

struct IFileFormat
{
    virtual void FileFormat() = 0;
};

class CWindows : public ISystem
{
public:
    virtual void Os() { std::cout << "Microsoft Windows" << std::endl; }
};

class CLinux : public ISystem
{
public:
    virtual void Os() { std::cout << "Ubuntu Linux" << std::endl; }
};

class CPe : public IFileFormat
{
public:
    virtual void FileFormat() { std::cout << "PE" << std::endl; }
};

class CElf : public IFileFormat
{
public:
    virtual void FileFormat() { std::cout << "ELF" << std::endl; }
};

//////////////////////////////////////////////////////////////////
//  Product Factory
//////////////////////////////////////////////////////////////////

struct IOsFactory
{
    virtual ISystem* CreateSystemInstance() = 0;
    virtual IFileFormat* CreateFileFormatInstance() = 0;
};

class CWindowsFactory : public IOsFactory
{
public:
    virtual ISystem* CreateSystemInstance() { return new CWindows(); }
    virtual IFileFormat* CreateFileFormatInstance() { return new CPe(); }
};

class CLinuxFactory : public IOsFactory
{
public:
    virtual ISystem* CreateSystemInstance() { return new CLinux(); }
    virtual IFileFormat* CreateFileFormatInstance() { return new CElf(); }
};

//////////////////////////////////////////////////////////////////
//  Installer
//////////////////////////////////////////////////////////////////

class COsInstaller
{
    ISystem* m_lpSystem;
    IFileFormat* m_lpFileFormat;
    IOsFactory* m_lpOs;

public:
    COsInstaller()
    {
        m_lpSystem = nullptr;
        m_lpFileFormat = nullptr;
        m_lpOs = nullptr;
    }

    ~COsInstaller()
    {
        if (m_lpSystem)
            delete m_lpSystem;

        if (m_lpFileFormat)
            delete m_lpFileFormat;

        if (m_lpOs)
            delete m_lpOs;
    }

public:
    void Install(IOsFactory* lpOs)
    {
        m_lpSystem = lpOs->CreateSystemInstance();
        m_lpFileFormat = lpOs->CreateFileFormatInstance();
        m_lpOs = lpOs;

        m_lpSystem->Os();
        m_lpFileFormat->FileFormat();
    }
};
